#ifndef GAME_ANIMATION_STREAMSCAPE_STREAMSCAPE_H
#define GAME_ANIMATION_STREAMSCAPE_STREAMSCAPE_H
//
// Game Animation.
// Copyright (C) 2007 Department of Computer Science, University of Copenhagen
//
#include <animation/configuration.h>

#include <OpenTissue/gpu/cg/cg_program.h>
#include <OpenTissue/gpu/image/image.h>
#include <OpenTissue/gpu/image/io/image_read.h>
#include <OpenTissue/gpu/image/io/image_write.h>
#include <OpenTissue/gpu/texture/texture_texture2D.h>
#include <OpenTissue/gpu/texture/texture_create_texture2D.h>

#include <cassert>

namespace animation
{

  template<typename types>
  class StreamScape
  {
  public:

    typedef boost::shared_ptr< OpenTissue::image::Image<unsigned char> > image_pointer;

  protected:

    typedef typename types::vector3_type             vector3_type;
    typedef typename vector3_type::value_type        real_type;
    typedef typename types::geometry_type            geometry_type;
    typedef typename geometry_type::vertex_type      vertex_type;

  protected:

    geometry_type     m_geometry;
    real_type         m_du;
    real_type         m_dv;
    real_type         m_dx;
    real_type         m_dy;
    real_type         m_ox;
    real_type         m_oy;

    OpenTissue::cg::Program         m_vertex_program;
    OpenTissue::cg::Program         m_fragment_program;
    image_pointer                   m_height_image;

    OpenTissue::texture::texture2D_pointer m_height_texture;

  public:

    void render()
    {
      pre_render();
      OpenTissue::gl::gl_check_errors("Before geometry render");
      m_geometry.vertex_object_render();
      OpenTissue::gl::gl_check_errors("After geometry render");
      post_render();
    }

  protected:

    void pre_render()
    {
      //--- Load textures if they are not allready loaded
      if(!m_height_image)
      {
        m_height_image.reset( new OpenTissue::image::Image<unsigned char>() );
        OpenTissue::image::read(demo_path + "streamscape/data/height.png",*m_height_image);
        
        m_height_texture = m_height_image->create_texture(GL_RGBA);
        m_height_texture->set_repeating(true);

        vector3_type min_coord(-10,-10,0);
        vector3_type max_coord( 10, 10,0);
        create_scape_geometry(min_coord,max_coord);
        m_geometry.cleanup_vertex_object();
      }

      if(!m_vertex_program.is_program_loaded())
      {
        m_vertex_program.load_from_file(OpenTissue::cg::Program::vertex_program, demo_path +"../animation/streamscape/vp_streamscape.cg");
      }
      m_vertex_program.enable();
      m_vertex_program.set_modelview_projection_matrix();
      m_vertex_program.set_float_param("du", m_du);
      m_vertex_program.set_float_param("dv", m_dv);
      m_vertex_program.set_input_texture( "height_texture",   m_height_texture   );

      //--- load fragment program
      if(!m_fragment_program.is_program_loaded())
      {
        m_fragment_program.load_from_file(OpenTissue::cg::Program::fragment_program, demo_path + "../animation/streamscape/fp_streamscape.cg");
      }
      m_fragment_program.enable();

      //--- Setup openGL state
      glEnable(GL_CULL_FACE);
    }

    void post_render()
    {
      glDisable(GL_CULL_FACE);
      m_fragment_program.disable();
      m_vertex_program.disable();
    }


    void create_scape_geometry(
        vector3_type const & min_coord
      , vector3_type const & max_coord
      )
    {

      int I = m_height_image->width();
      int J = m_height_image->height();
      int M = I+1;  //--- number of vertices along x-axis
      int N = J+1;  //--- number of vertices along y-axis

      unsigned int vertices = M*N;
      unsigned int indices  = I*J*6;
      if(m_geometry.vertices() != vertices)
      {
        m_geometry = geometry_type(vertices,indices);
      }

      vector3_type range = max_coord - min_coord;

      m_du = 1.0/I;
      m_dv = 1.0/J;
      m_dx = range(0) / I;   
      m_dy = range(1) / J;   
      m_ox = min_coord(0);
      m_oy = min_coord(1);

      int k = 0;
      for(int j=0;j<J;++j)
      {
        for(int i=0;i<I;++i)
        {
          m_geometry.get_index(k++) =     j *M + i;
          m_geometry.get_index(k++) =     j *M + i+1;
          m_geometry.get_index(k++) =  (j+1)*M + i+1;
          m_geometry.get_index(k++) =     j *M + i;
          m_geometry.get_index(k++) =  (j+1)*M + i+1;
          m_geometry.get_index(k++) =  (j+1)*M + i;
        }
      }
      for(int j=0;j<N;++j)
      {
        for(int i=0;i<M;++i)
        {
          vertex_type & vertex = m_geometry.get_vertex(j*M + i);
          vertex.m_coord(0) = i*m_dx + m_ox; 
          vertex.m_coord(1) = j*m_dy + m_oy; 
          vertex.m_coord(2) = 0; 
          vertex.m_tex0[0]  = (i+0.5)*m_du;
          vertex.m_tex0[1]  = (j+0.5)*m_dv;
        }
      }
    }

  };

} // namespace animation

// GAME_ANIMATION_STREAMSCAPE_STREAMSCAPE_H
#endif
